package com.ouwen.smartpay.service.chain.slots.service;

import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.config.utils.ReferenceConfigCache;
import com.alibaba.dubbo.remoting.TimeoutException;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.service.GenericService;
import com.fm.common.emuns.ResponseCode;
import com.fm.core.common.boot.Apps;
import com.fm.core.common.facade.BizResultBase;
import com.fm.core.common.facade.MerchResultBase;
import com.fm.core.common.facade.OrderBase;
import com.fm.core.utils.Strings;
import com.ouwen.smartpay.domain.MetaDataWarpper;
import com.ouwen.smartpay.domain.ProviderWarpper;
import com.ouwen.smartpay.domain.ServiceWarpper;
import com.ouwen.smartpay.openapi.order.ServiceOrder;
import com.ouwen.smartpay.openapi.result.ServiceResult;
import com.ouwen.smartpay.service.chain.context.Context;
import com.ouwen.smartpay.service.chain.slotchain.AbstractLinkedProcessorSlot;
import com.ouwen.smartpay.service.chain.spi.SpiOrder;
import com.ouwen.smartpay.service.dubbo.RegistryContainer;
import com.ouwen.smartpay.utils.BeanUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

import static com.ouwen.smartpay.service.chain.spi.SpiOrder.LOWEST_PRECEDENCE;


/**
 * @author <a href="http://youngitman.tech">青年IT男</a>
 * @version v1.0.0
 * @className ServiceSlot
 * @description 调用服务提供者
 * @JunitTest: {@link  }
 * @date 2020-04-22 22:49
 **/
@SpiOrder(LOWEST_PRECEDENCE)
public class ServiceSlot extends AbstractLinkedProcessorSlot<ServiceOrder> {

    Logger logger = LoggerFactory.getLogger(ServiceSlot.class);

    private RegistryContainer registryContainer;

    @Override
    public void entry(Context context, ServiceOrder order, Object... args) throws Throwable {

        try {

            ServiceResult serviceResult = (ServiceResult) args[0];

            registryContainer = Apps.getApplicationContext().getBean(RegistryContainer.class);

            ServiceWarpper serviceWarpper = ServiceWarpper.builder().group(Strings.isEmpty(order.getGroup()) ? "" : order.getGroup())
                    .version(order.getVersion()).serviceName(order.getService()).build();

            //查找服务提供者信息
            ProviderWarpper providerWarpper = registryContainer.findServiceInterfaceClassPath(serviceWarpper);

            if(providerWarpper == null){

                logger.warn("openapi提供的服务信息ProviderWarpper未查询到服务【{}】- 版本【{}】- 分组【{}】", order.getService(), order.getVersion(), order.getGroup());

                exit(context, args);

                return;

            }

            //填充接口名称
            serviceWarpper.setInterfaceName(providerWarpper.getInterfaceClass().getName());

            if (registryContainer.getDefalutProviderWarpper() == providerWarpper) {

                MerchResultBase bizResult = buildFailResult(context);

                serviceResult.setDatas(BeanUtil.transBean2Map(bizResult));

                logger.warn("openapi提供的服务信息ProviderWarpper未查询到服务【{}】- 版本【{}】- 分组【{}】", order.getService(), order.getVersion(), order.getGroup());

                exit(context, args);

                return;
            }

            //获取ReferenceConfig
            MetaDataWarpper metaDataWarpper = registryContainer.findMetaDataWarpper(serviceWarpper);

            if (registryContainer.getDefalutMetaDataWarpper() == metaDataWarpper) {

                MerchResultBase bizResult = buildFailResult(context);

                serviceResult.setDatas(BeanUtil.transBean2Map(bizResult));

                logger.warn("openapi提供服务ReferenceConfig未查询到: 【{}】- 版本【{}】- 分组【{}】", order.getService(), order.getVersion(), order.getGroup());

                exit(context, args);

                return;
            }

            ReferenceConfig<GenericService> referenceConfig = metaDataWarpper.getReferenceConfig();

            ReferenceConfigCache cache = ReferenceConfigCache.getCache();

            GenericService genericService = cache.get(referenceConfig);

            //转换为服务提供者的Order
            OrderBase busOrder = context.getOrder();

            // 调用服务
            Map resultMap = (Map) genericService.$invoke(providerWarpper.getMethod().getName(), new String[]{providerWarpper.getOrder().getName()}, new Object[]{busOrder});

            Class<?> resultClass = providerWarpper.getResult();

            MerchResultBase bizResult = (MerchResultBase) resultClass.newInstance();

            BeanUtil.transMap2Bean(resultMap, bizResult);

            fireEntry(context, order, args, bizResult);

        } catch (RpcException e) {

            logger.error("RPC调用内部系统异常:", e);

            BizResultBase result = new BizResultBase();

            result.setRespCode(ResponseCode.FAIL.code());

            result.setRespMsg(ResponseCode.FAIL.message());

            result.setDescription(e.getMessage());

            exit(context, order, args, result);

        } catch (Exception e) {

            logger.error("业务服务调用异常:", e);

            BizResultBase result = new BizResultBase();

            if (e instanceof TimeoutException) {

                result.setRespCode(ResponseCode.PROCESSING.code());

                result.setRespMsg(ResponseCode.PROCESSING.message());

                result.setDescription("内部通信超时,请重试业务");

            } else {

                result.setRespCode(ResponseCode.FAIL.code());

                result.setRespMsg(ResponseCode.FAIL.message());

                result.setDescription(e.getMessage());

            }

            exit(context, order, args, result);

        }
    }

    private MerchResultBase buildFailResult(Context context) {

        MerchResultBase bizResult = new MerchResultBase();

        bizResult.setRespMsg(ResponseCode.OPENAPI_UNSERVICE.message());

        bizResult.setRespCode(ResponseCode.OPENAPI_UNSERVICE.code());

        bizResult.setDescription("未查找到服务");

        context.setResult(bizResult);

        return bizResult;

    }

    @Override
    public void exit(Context context, Object... args) {

        fireExit(context, args);

    }
}
